/* 
 * File:   SMP.c
 * Author: Vagner Clementino
 *
 * Created on 11 de Março de 2012, 13:25
 */

#include <stdio.h>

#include "SMP.h"
#include "in_out.h"

void smp(Lista lista_homens, Lista lista_mulheres, int num_individuos) {


    FILE* saida = abre_arquivo(OUTFILE, "a");
    float satis_masculina;
    float satis_feminina;
    float satis_geral;

    casamento_estavel(lista_homens, lista_mulheres, num_individuos);
    imprime_casamentos(lista_homens, saida);

    satis_masculina = calcula_satisfabilidade(lista_homens);
    satis_feminina = calcula_satisfabilidade(lista_mulheres);

    satis_geral = (satis_masculina + satis_feminina) / 2;

    imprime_satisfabilidade(satis_geral, satis_masculina, satis_feminina, saida);
    fecha_arquivo(saida);
}

void casamento_estavel(Lista lista_homens, Lista lista_mulheres, int num_individuos) {

    Homem h;
    Mulher m;
    matriz_casamento matriz;
    int rounds = 0;
    int id_homem = 0;
    /*A variavel id_pretedente contera o id da pretendente
     * de um determinado homem que esta mais bem posicionada
     * na lista de preferencia e para o qual ainda nao se tentou
     * criar o casal.
     */
    int id_pretedente;

    /*Estado civil contera SOLTEIRO (-1) caso o mulher e mulher  em questao
     * estiver solteira ou id com o qual aquela pessoa esta casada.
     */
    int estado_civil;

    matriz = cria_matriz(num_individuos);

   


    while (tem_solteiro(matriz, num_individuos)) {

        id_homem = rounds % num_individuos;

        h = busca_pessoa(id_homem + 1, lista_homens);

        if (esta_solteiro(id_homem, 'h', matriz, num_individuos) == SOLTEIRO) {
            /*Se o homem de id i esta solteiro
             * busque-o na lista de homens e descubra qual o id
             * da mulher mais bem posicionada na sua lista de preferencia
             * para o qual ele ainda nao propos casamento.
             */
            
            id_pretedente = retorna_pretendente(h);
           
            m = (Mulher) busca_pessoa(id_pretedente-1, lista_mulheres);

            /*Descobre o estado civil da pretedente da mulher
             * de id id_pretendente
             */
            
            estado_civil = esta_solteiro(id_pretedente, 'm', matriz, num_individuos);

            if (estado_civil == SOLTEIRO) {
                /*Se a mulher estiver solteira ela ficara parcialmente
                 * casada com o homem de id i.
                 */
               matriz[id_homem][id_pretedente - 1] = 1;
                
            } else {
                if (grau_de_preferencia((Pessoa*) m, id_homem + 1) <
                    grau_de_preferencia((Pessoa*) m, estado_civil + 1)) {

                    /*Se o grau de preferencia do homem de id i
                     * for menor do que o grau de preferencia
                     * do atual parceiro de sua pretedente, 
                     * faca o homem de id i casal com a mulher id_pretedente
                     * e terminando o 'casamento anterior 
                     */

                    matriz [id_homem][id_pretedente - 1] = 1;
                    matriz [estado_civil] [id_pretedente - 1] = 0;
                }
            }
        }

        rounds++;
    }
    define_casamentos(matriz, num_individuos, lista_homens, lista_mulheres);
    desaloca_matriz(matriz, num_individuos);
}

void define_casamentos(matriz_casamento matriz, int tam_matriz, Lista homens, Lista mulheres) {
    int i, j;
    Homem h;
    Mulher m;
    for (i = 0; i < tam_matriz; i++) {
        for (j = 0; j < tam_matriz; j++) {
            if (matriz [i][j] == 1) {
                /*O homem i esta casado com a mulher j.*/
                h = busca_pessoa(i + 1, homens);
                m = busca_pessoa(j + 1, mulheres);
                if (h != NULL && m != NULL) {
                    h->conjuge = j + 1;
                    m->conjuge = i + 1;
                }
            }
        }
    }
}

float calcula_satisfabilidade(const Lista l) {
    Nodo* aux;
    float sast = 0.0;

    if (esta_vazia(l)) {
        printf("Lista vazia.\n");
        return sast;
    } else {
        aux = l->primeiro;
        while (aux != NULL) {
            sast += (grau_de_preferencia(&(aux->p), aux->p.conjuge) + 1.0);
            aux = (Nodo*) aux->next;
        }
        return (sast / l->num_itens);
    }
}

void imprime_casamentos(Lista l, FILE* saida) {
    Nodo* aux;

    if (esta_vazia(l)) {
        printf("Lista vazia.\n");
        return;
    } else {
        aux = l->primeiro;
        while (aux != NULL) {
            fprintf(saida, "%d %d\n", aux->p.id, aux->p.conjuge);
            aux = (Nodo*) aux->next;
        }
    }
}

void imprime_satisfabilidade(float satis_geral, float satis_homens, float satis_mulheres, FILE* saida) {
    fprintf(saida, "%.2f\n", satis_geral);
    fprintf(saida, "%.2f\n", satis_homens);
    fprintf(saida, "%.2f\n", satis_mulheres);
}